package com.xpgk.interceptor;

import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.jfinal.aop.Interceptor;
import com.jfinal.aop.Invocation;
import com.jfinal.kit.PropKit;
import com.jfinal.kit.StrKit;
import com.jfinal.log.Log;
import com.xpgk.constant.ConstantAuth;
import com.xpgk.constant.ConstantInit;
import com.xpgk.constant.ConstantWebContext;
import com.xpgk.mvc.base.BaseController;
import com.xpgk.mvc.deptStation.DeptStationService;
import com.xpgk.mvc.right.Right;
import com.xpgk.mvc.user.User;
import com.xpgk.tools.ToolDateTime;
import com.xpgk.tools.ToolWeb;
import com.xpgk.tools.security.ToolIDEA;

/**
 * 权限认证拦截器 描述： 1.处理权限验证 2.处理全局异常 3.处理权限相关的工具类方法
 */
public class AuthInterceptor implements Interceptor {

	private static final Log log = Log.getLog(AuthInterceptor.class);

	@Override
	public void intercept(Invocation invoc) {
		BaseController contro = (BaseController) invoc.getController();
		HttpServletRequest request = contro.getRequest();
		HttpServletResponse response = contro.getResponse();

		log.debug("获取用户请求的URI，两种形式，参数传递和直接request获取");
		String uri = invoc.getActionKey(); // 默认就是ActionKey
		if (!uri.startsWith(ConstantWebContext.request_app)) {

			if (invoc.getMethodName().equals(ConstantWebContext.request_toUrl)) {
				uri = ToolWeb.getParam(request, ConstantWebContext.request_toUrl); // 否则就是toUrl的值
			}

			log.debug("获取当前用户!");
			boolean userAgentVali = true; // 是否验证userAgent，默认是
			if (uri.equals("/platform/ueditor") || uri.equals("/platform/upload")) { // 针对ueditor特殊处理，flash上传userAgent和浏览器并不一致
				userAgentVali = false;
			}
			User user = getCurrentUser(request, response, userAgentVali);// 当前登录用户
			if (null != user) {
				contro.setAttr(ConstantWebContext.request_cUser, user);
				contro.setAttr(ConstantWebContext.request_cUserIds, user.getPKValue());
			}

			log.debug("获取URI对象!");
			Right right = Right.cacheGet(uri);

			log.debug("判断URI是否存在!");
			if (null == right) {
				log.debug("URI不存在!uri = " + uri);
				toView(contro, ConstantAuth.auth_no_url, "权限认证过滤器检测：URI不存在" + uri);
				return;
			} else {
				log.debug("URI存在!");
				contro.setAttr(Right.column_ids, right.getPKValue());
			}

			log.debug("是否需要权限验证!");
			if (right.get(Right.column_privilegess).equals("1")) {
				log.debug("需要权限验证!");
				if (user == null) {
					log.debug("权限认证过滤器检测:未登录!");
					toView(contro, ConstantAuth.auth_no_login, "权限认证过滤器检测：未登录");
					return;
				}

				if (!hasPrivilegeUrl(right.getPKValue(), user.getPKValue())) {// 权限验证
					log.debug("权限验证失败，没有权限!");
					toView(contro, ConstantAuth.auth_no_permissions, "权限验证失败，您没有操作权限");
					return;
				}
			}
		}
		try {
			invoc.invoke();
		} catch (Exception e) {

			String expMessage = e.getMessage();
			// 开发模式下的异常信息
			if (Boolean.parseBoolean(PropKit.get(ConstantInit.config_devMode))) {
				e.printStackTrace();
				ByteArrayOutputStream buf = new ByteArrayOutputStream();
				e.printStackTrace(new PrintWriter(buf, true));
				expMessage = buf.toString();
			}

			log.error("返回失败提示页面!Exception = " + expMessage);
			toView(contro, ConstantAuth.auth_exception, e.getMessage());
		}
	}

	/**
	 * 提示信息展示页
	 * 
	 * @param contro
	 * @param type
	 * @param msg
	 */
	private void toView(BaseController contro, String type, String msg) {
		String isAjax = contro.getRequest().getHeader("X-Requested-With");

		if (type.equals(ConstantAuth.auth_no_login)) {// 未登录处理
			contro.render("/common/logout.html");
			return;
		}

		contro.setAttr("msg", msg);
		if (ConstantAuth.auth_exception.equals(type)) {// 系统异常
			contro.setAttr("title", "系统异常");
			contro.render("/common/msgAjax.html");
		} else {
			if (isAjax != null && isAjax.equalsIgnoreCase("XMLHttpRequest")) {
				contro.setAttr("code", 300);
				contro.renderJson();
			} else {
				contro.setAttr("title", "操作失败");
				contro.render("/common/msgAjax.html");
			}
		}

		// if(type.equals(ConstantAuth.auth_exception) ||
		// type.equals(ConstantAuth.auth_no_permissions) ||
		// type.equals(ConstantAuth.auth_no_url)){
		// contro.setAttr("msg", msg);
		// contro.setAttr("code", 300);
		// contro.renderJson();
		// return;
		// }

		// if (isAjax != null && isAjax.equalsIgnoreCase("XMLHttpRequest")) {
		// contro.render("/common/msgAjax.html"); // Ajax页面
		// } else {
		// contro.render("/common/msg.html"); // 完整html页面
		// }
	}

	/**
	 * 判断用户是否拥有某个功能的操作权限
	 * 
	 * @param operatorIds
	 * @param userIds
	 * @return
	 */
	public static boolean hasPrivilegeUrl(String rightIds, String userIds) {

		/**
		 * 取缓存验证操作权限
		 **/
		// 根据岗位查询权限
		User user = User.cacheGetByUserId(userIds);

		String stationIds = user.get(User.column_stationids);
		List<Right> rList = DeptStationService.cacheGet(stationIds);
		for (Right right : rList) {
			if (rightIds.equals(right.getPKValue())) {
				return true;
			}
		}

		return false;
	}

	/**
	 * 获取当前登录用户
	 * 
	 * @param request
	 * @param response
	 * @param userAgentVali
	 *            是否验证 User-Agent
	 * @return
	 */
	public static User getCurrentUser(HttpServletRequest request, HttpServletResponse response, boolean userAgentVali) {
		String cxtPath = request.getContextPath();
		if (StrKit.isBlank(cxtPath)) {
			cxtPath = "/";
		}

		// 加密串存储位置，默认先取header
		String store = ConstantAuth.auth_store_header;
		String loginCookie = request.getHeader(ConstantWebContext.cookie_authmark);
		if (StrKit.isBlank(loginCookie)) {
			// 如果为空，再取cookie
			store = ConstantAuth.auth_store_cookie;
			loginCookie = ToolWeb.getCookieValueByName(request, ConstantWebContext.cookie_authmark);
		}

		// 处理加密串的解析和验证
		if (null != loginCookie && !loginCookie.equals("")) {
			// 1.解密认证数据
			String outer = ToolIDEA.decrypt(loginCookie);
			if (StrKit.isBlank(outer)) {
				ToolWeb.addCookie(response, "", cxtPath, true, ConstantWebContext.cookie_authmark, null, 0);
				return null;
			}

			// 2. 分解认证数据
			String userIds = null;
			String secretkey = null;
			long loginDateTimes;
			String ips = null;
			String userAgent = null;
			boolean autoLogin = false;
			try {
				String[] outerArr = outer.split(".#."); // arr[0]：USERID，arr[1]：inner
				userIds = outerArr[0];
				User user = User.cacheGetByUserId(userIds);
				secretkey = user.getStr(User.column_secretkey);
				String inner = ToolIDEA.decrypt(secretkey, outerArr[1]);
				String[] innerArr = inner.split(".#."); // arr[0]：时间戳，arr[1]：USER_IP，
														// arr[2]：USER_AGENT

				loginDateTimes = Long.parseLong(innerArr[0]); // 时间戳
				ips = innerArr[1]; // ip地址
				userAgent = innerArr[2]; // USER_AGENT
				autoLogin = Boolean.valueOf(innerArr[3]); // 是否自动登录
			} catch (Exception e) {
				if (store.equals(ConstantAuth.auth_store_cookie)) {
					ToolWeb.addCookie(response, "", cxtPath, true, ConstantWebContext.cookie_authmark, null, 0);
				}
				return null;
			}

			// 3.用户当前数据
			String newIp = ToolWeb.getIpAddr(request);
			String newUserAgent = request.getHeader("User-Agent");

			Date start = ToolDateTime.getDate();
			start.setTime(loginDateTimes); // 用户自动登录开始时间
			int day = ToolDateTime.getDateDaySpace(start, ToolDateTime.getDate()); // 已经登录多少天

			int maxAge = PropKit.getInt(ConstantInit.config_maxAge_key);

			// 4. 验证数据有效性
			if (ips.equals(newIp) && (userAgentVali ? userAgent.equals(newUserAgent) : true) && day <= maxAge) {
				// 如果不记住密码，单次登陆有效时间验证
				if (!autoLogin) {
					int minute = ToolDateTime.getDateMinuteSpace(start, new Date());
					int session = PropKit.getInt(ConstantInit.config_session_key);
					if (minute > session) {
						return null;
					} else {
						// 重新生成认证cookie，目的是更新时间戳
						long dateNew = ToolDateTime.getDateByTime();

						// 内层用户私有密钥加密
						StringBuilder innerSb = new StringBuilder(); // 时间戳.#.USER_IP.#.USER_AGENT.#.autoLogin
						innerSb.append(dateNew).append(".#.").append(ips).append(".#.").append(userAgent).append(".#.")
								.append(autoLogin);
						String innerNew = ToolIDEA.encrypt(secretkey, innerSb.toString());

						// 外层使用系统公共密钥加密
						StringBuilder outerSb = new StringBuilder(); // userIds.#.inner
						outerSb.append(userIds).append(".#.").append(innerNew);
						String outerNew = ToolIDEA.encrypt(outerSb.toString());

						// 添加到Cookie
						if (store.equals(ConstantAuth.auth_store_cookie)) {
							int maxAgeTemp = -1; // 设置cookie有效时间
							ToolWeb.addCookie(response, "", cxtPath, true, ConstantWebContext.cookie_authmark, outerNew,
									maxAgeTemp);

						} else if (store.equals(ConstantAuth.auth_store_header)) {
							response.setHeader(ConstantWebContext.cookie_authmark, outerNew);
						}
					}
				}

				// 返回用户数据
				return User.cacheGetByUserId(userIds);
			}
		}

		return null;
	}

	/**
	 * 设置当前登录用户到cookie
	 * 
	 * @param request
	 * @param response
	 * @param user
	 * @param autoLogin
	 *            描述：加密串分两层，外层使用系统公共密钥加密，内层使用用户私有密钥加密
	 */
	public static void setCurrentUser(HttpServletRequest request, HttpServletResponse response, User user,
			boolean autoLogin) {
		String cxtPath = request.getContextPath();
//		System.out.println("1111111111111111111111111111s"+cxtPath);
		if (StrKit.isBlank(cxtPath)) {
			cxtPath = "/";
		}
//		System.out.println("22222222222222222222222222222222"+cxtPath);
		// 1.设置cookie有效时间
		int maxAgeTemp = -1;
		if (autoLogin) {
			maxAgeTemp = PropKit.getInt(ConstantInit.config_maxAge_key);
		}
//		System.out.println("3333333333333333333333333333333"+maxAgeTemp);
		// 2.设置用户名到cookie
		ToolWeb.addCookie(response, "", cxtPath, true, "userName", user.getStr(User.column_username), maxAgeTemp);

		// 3.生成登陆认证cookie
		String secretkey = user.getStr(User.column_secretkey);
		String userIds = user.getPKValue();

		String ips = ToolWeb.getIpAddr(request);
		String userAgent = request.getHeader("User-Agent");
		long date = ToolDateTime.getDateByTime();

		// 内层用户私有密钥加密
		StringBuilder innerSb = new StringBuilder(); // 时间戳.#.USER_IP.#.USER_AGENT.#.autoLogin
		innerSb.append(date).append(".#.").append(ips).append(".#.").append(userAgent).append(".#.").append(autoLogin);
		String inner = ToolIDEA.encrypt(secretkey, innerSb.toString());
//		System.out.println("555555555555555555555555555"+inner);
		// 外层使用系统公共密钥加密
		StringBuilder outerSb = new StringBuilder(); // userIds.#.inner
		outerSb.append(userIds).append(".#.").append(inner);
		String outer = ToolIDEA.encrypt(outerSb.toString());
//		System.out.println("666666666666666666666666666666666"+outer);
		// 4. 添加到Cookie和header
		ToolWeb.addCookie(response, "", cxtPath, true, ConstantWebContext.cookie_authmark, outer, maxAgeTemp);
		response.setHeader(ConstantWebContext.cookie_authmark, outer);
	}

	/**
	 * 设置验证码
	 * 
	 * @param request
	 * @param response
	 * @param authCode
	 */
	public static void setAuthCode(HttpServletRequest request, HttpServletResponse response, String authCode) {
		String cxtPath = request.getContextPath();
		if (StrKit.isBlank(cxtPath)) {
			cxtPath = "/";
		}

		// 1.生成验证码加密cookie
		String authCodeCookie = ToolIDEA.encrypt(authCode);

		// 2.设置登陆验证码cookie
		int maxAgeTemp = 30 * 60; // 半小时
		ToolWeb.addCookie(response, "", cxtPath, true, ConstantWebContext.request_authCode, authCodeCookie, maxAgeTemp);
	}

	/**
	 * 获取验证码
	 * 
	 * @param request
	 * @param response
	 * @return
	 */
	public static String getAuthCode(HttpServletRequest request, HttpServletResponse response) {
		String cxtPath = request.getContextPath();
		if (StrKit.isBlank(cxtPath)) {
			cxtPath = "/";
		}

		// 1.获取cookie加密数据
		String authCode = ToolWeb.getCookieValueByName(request, ConstantWebContext.request_authCode);

		// 2.获取验证码后清除客户端验证码信息
		ToolWeb.addCookie(response, "", cxtPath, true, ConstantWebContext.request_authCode, null, 0);

		// 3.解密数据
		if (null != authCode && !authCode.equals("")) {
			authCode = ToolIDEA.decrypt(authCode);
			return authCode;
		}

		return null;
	}

}
